package com.ifast.demo.controller;
import cn.hutool.core.io.resource.ClassPathResource;
import com.alibaba.fastjson.JSON;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.springframework.util.ClassUtils;
import org.xbill.DNS.ARecord;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.Resolver;
import org.xbill.DNS.SimpleResolver;

import java.io.*;
import java.net.InetAddress;
import java.sql.Timestamp;
import java.util.*;

/**
 * 子域名嗅探器，使用时将domain修改为想要嗅探
 * 的域名调用相应的方法即可开始嗅探，嗅探到之
 * 后将会以标准输出的形式输出子域名默认页面的
 * 标题和完整域名
 *
 * @author AnonymousHu
 */
public class FindSubDomain {
    //根域名
    private  String domain = "byted.org";
    //超时时间，会极大的影响性能但是过小的timeout又可能导致部分延迟过高的子站被漏，
    private  int timeOut = 1000;
    private  double curCount = 0;
    private  double totalCount = 0;
    private  long curtime;
    public   Vector<String> subdict;

    private List<Map<String,Object>> domains = new ArrayList<>();
    
    public  double getProgress()
    {
    	return curCount*100/totalCount;
    }
    
    
    public  int startEnumSub(int lvl)
    {
    	totalCount = 0;
    	curCount = 0;
    	curtime = System.currentTimeMillis();
    	
    	for(int i = 0; i <= lvl; i++)
       	{
    		totalCount += Math.pow(36, i);
    	}
    	if(lvl == 1) {
    		try {
				FindSubIn1("");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}else if(lvl == 2) {
    		try {
        		FindSubIn1("");
				FindSubIn2("");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}  		
    	}else if(lvl == 3) {
    		try {
        		FindSubIn1("");
				FindSubIn2("");
	    		FindSubIn3("");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 

    	}else if(lvl > 3)
    	{
    		try {
				FindSubIn1("");
	    		FindSubIn2(""); 
	    		FindSubIn3("");
	    		for(int  k = 4; k < lvl; k++) {
	        		FindSubInLevel("", k);
	    		}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

    	}else {
    		return 1;
    	}
    	return 0;
    }

    public  String getDomain() {
        return domain;
    }

    public  void setDomain(String domain) {
        this.domain = domain;
    }

    private  ResolverSlct rs = new ResolverSlct();
    /**
     * 判断子域名，可用则返回子域名默认页面标题，否则返回404
     *
     * @param prefix 子域名前缀
     * @return 子域名默认页面标题或404
     * @throws Exception 
     */
    public  String findip(String prefix) throws Exception {
    	
		/*
		 * try { Document doc = Jsoup.connect("http://" + prefix + "." +
		 * domain).timeout(timeOut).get(); return doc.title(); } catch (IOException e) {
		 * return "404"; }
		 */
    	curCount++;
    	if(curCount%1000 == 0) {
    		System.out.println("current prefix: "
    							+ prefix
    							+ " count: "
    							+ curCount 
    							+ " progress : "
    							+ getProgress() 
    							+ "%" 
    							+ " elapsed: "
    							+ (System.currentTimeMillis() - curtime)/1000
    							+ " 秒");
    	}
    	/* method 1 */
    	if(curCount%3 == 0) {
        	rs.SetDomainPrefix(prefix);
        	String svr = rs.Selector();
        	Resolver resolver = new SimpleResolver(svr);
        	resolver.setPort(53);
        	Lookup lookup = new Lookup(prefix + "." + domain);
        	lookup.setResolver(resolver);
        	Record[] records = lookup.run();
        	if(lookup.getResult() == Lookup.SUCCESSFUL) {
        		ARecord ar = (ARecord) records[0];
        		InetAddress addr = ar.getAddress();
        		return addr.getHostAddress();      	
        	}
    	}else
    	{
        	/* method 2*/
    		IPConvertTask ipc = new IPConvertTask(prefix+"." + domain);
    		return ipc.Convert();
    	}


    	return "404";
    }

    /**
     * 查找长度为一个字符的子域名
     *
     * @param prf 前缀，可留空
     * @throws Exception 
     */
    public  void FindSubIn1(String prf) throws Exception {
        for (char a = 'a'; a <= 'z'; a++) {
            String prefix = prf + a;
            String title = findip(prefix);
            if (title != "404") {
                System.out.println(prefix + "." + domain + " : " + title);
                Map<String,Object>map = new HashMap<>();
                map.put("domain",prefix + "." + domain);
                map.put("ip",title);
                domains.add(map);
                PermaKeep(prefix + "." + domain, title);
            }
        }
        for (char a = '0'; a <= '9'; a++) {
            String prefix = prf + a;
            String title = findip(prefix);
            if (title != "404") {
                System.out.println(prefix + "." + domain + " : " + title);
                Map<String,Object>map = new HashMap<>();
                map.put("domain",prefix + "." + domain);
                map.put("ip",title);
                domains.add(map);
                PermaKeep(prefix + "." + domain, title);
            }
        }
        
    }

    /**
     * 查找长度为二个字符的子域名
     *
     * @param prf 前缀，可留空
     * @throws Exception 
     */
    public  void FindSubIn2(String prf) throws Exception {
        for (char a = 'a'; a <= 'z'; a++) {
            FindSubIn1("" + prf + a);
        }
        for (char a = '0'; a <= '9'; a++) {
            FindSubIn1("" + prf + a);
        }
    }

    /**
     * 查找长度为三个字符的子域名
     *
     * @param prf 前缀，可留空
     * @throws Exception 
     */
    public  void FindSubIn3(String prf) throws Exception {
        for (char a = 'a'; a <= 'z'; a++) {
            FindSubIn2(prf + a);
        }
    }

    /**
     * 查找长度为四个字符的子域名
     *
     * @param prf 前缀，可留空
     */
    public  void FindSubIn4(String prf) {
        for (char x = 'a'; x <= 'z'; x++) {
            new FindSubIn3Thread(prf + x).start();
        }
    }


    /**
     * 查找长度为一个字符的子域名
     * 调用两层查找3位子域名线程
     *
     * @param prf 前缀，可留空
     */
    public  void FindSubInLevel(String prf, int level) {
        for (char x = 'a'; x <= 'z'; x++) {
        	if(level > 3) {
        		new FindSubIn3Thread(prf + x).start();
        	}
        	else if(level > 1) {
                FindSubInLevel(prf + "" + x, level -1);        		
        	}else {
        		try {
					FindSubIn1(prf + "" + x);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        	}
        }
        for (char x = '0'; x <= '9'; x++) {
        	if(level > 3) {
        		new FindSubIn3Thread(prf + x).start();
        	}
        	else if(level > 1) {
                FindSubInLevel(prf + "" + x, level -1);        		
        	}else {
        		try {
					FindSubIn1(prf + "" + x);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        	}
        }
    }
    /**
     * （Facade模式应用）查找四位字符内的所有子域名
     * @throws Exception 
     */
    public  void FindSubIn123() throws Exception {
        FindSubIn1("");
        FindSubIn2("");
        FindSubIn3("");
    }
    /**
     * （Facade模式应用）查找四位字符内的所有子域名
     * @throws Exception 
     */
    public  void FindSubIn1234() throws Exception {
        FindSubIn1("");
        FindSubIn2("");
        FindSubIn3("");
        FindSubInLevel("", 4);
    }

    /**
     * （Facade模式应用）查找五位字符内的所有子域名
     * @throws Exception 
     */
    public  void FindSubIn12345() throws Exception {
        FindSubIn1("");
        FindSubIn2("");
        FindSubIn3("");
        FindSubIn4("");
        FindSubInLevel("", 5);
    }
    /**
     * 内部线程，用于执行嗅探
     */
    private  class FindSubIn3Thread extends Thread {
        String prf;

        FindSubIn3Thread(String prf) {
            this.prf = prf;
        }

        @Override
        public void run() {
            super.run();
            try {
				FindSubIn3(prf);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
    }
    
    public  void PermaKeep(String subdomain, String ip)
    {
    	// FIXME: 
    }
    /**
     * 快速策略，对比慢速策略是1秒5个。
     *https://github.com/rbsec/dnscan
     *  前缀，可留空
     * @throws Exception 
     */
    public  void FindbySheet() throws Exception {
        //获取文件的URL
        for (int i = 0; i < subdict.size(); i++) {

            String title = findip(subdict.get(i));
            if (title != "404") {
                System.out.println(subdict.get(i) + "." + domain + " : " + title);
                Map<String,Object>map = new HashMap<>();
                map.put("domain",subdict.get(i) + "." + domain);
                map.put("ip",title);
                domains.add(map);
                PermaKeep(subdict.get(i)+"." + domain, title);
            }
        }
    }
    public  void scanByPolicy(int n) {
    	if(n == 1) {
    		startEnumSub(3);
    	}else
    	{
    		try {
				FindbySheet();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    }
    public List<Map<String,Object>> getDomains(){

        return domains;
    }
    public  void readDict()
    {
    	subdict = new Vector<String>();

        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("subdomains-10000.txt");
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));//构造一个BufferedReader类来读取文件

            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法，一次读一行
                subdict.add(s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }

    }
    
    public static void main(String[] args) throws InterruptedException {
       FindSubDomain findSubDomain = new FindSubDomain();
         /*findSubDomain.setDomain("coconet.cn");
        try {
            //FindSubIn1("");
            //FindSubIn2("");
            //FindSubIn3("");
			//FindSubIn123();
            findSubDomain.startEnumSub(3);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
        findSubDomain.setDomain("coconet.cn");
        findSubDomain.readDict();

        try{
            findSubDomain.FindbySheet();
        }catch (Exception e){

        }
        //System.out.println("111");
    }


}
